﻿using Define;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;

namespace Tools.Base
{
    public enum LogTypes
    {
        Info = 0,
    }

    public class LogItem
    {
        public LogTypes type = LogTypes.Info;
        public DateTime tLog = DateTime.Now;
        public string strMsg;

        public string Content
        {
            get
            {
                return string.Format("[{0}] {1}", tLog, strMsg);
            }
        }
    }

    public class LogFilePoolItem
    {
        public LogFilePoolItem()
        {
            this._strCurFileName = "";
            this._nCurFileLength = 0L;
        }

        ~LogFilePoolItem()
        {
            if (null != this._StreamWriter)
            {
                try
                {
                    this._StreamWriter.Close();
                }
                catch
                {
                }
                this._StreamWriter = null;
            }
        }

        public string _strCurFileName;
        public long _nCurFileLength;
        public StreamWriter _StreamWriter;
        public long OpenTimeOnHours = -1L;
        public long OpenTimeOnDayOfYear = -1L;
    }

    public class LogManager
    {
        public static long LogFileMaxSize
        {
            get
            {
                return LogManager._logFileMaxSize;
            }
            set
            {
                LogManager._logFileMaxSize = value;
            }
        }

        public static string LogPath
        {
            get
            {
                if (_LogPath == string.Empty)
                {
                    _LogPath = PathDefine.LogPath;
                    if (!Directory.Exists(_LogPath))
                    {
                        Directory.CreateDirectory(_LogPath);
                    }
                }
                return _LogPath;
            }
        }

        private static void _WriteCPULog()
        {
            DateTime tNow = DateTime.Now;
            if ((tNow - _tLastWriteCPU).TotalMilliseconds > 60000L)
            {
                _tLastWriteCPU = tNow;
                SystemInfo.GetCPUValue(out float f, out string msg);
                WriteLog(string.Format("cpu usage ={0} msg={1}", f, msg), null, true);
            }
        }

        public static void ThreadFunc(object sender, EventArgs e)
        {
            _ThreadFunc();
            //_WriteCPULog();
        }

        private static void _ThreadFunc()
        {
            try
            {
                while (_LogItems.TryDequeue(out LogItem item))
                {
                    if (item != null)
                    {
                        LogFilePoolItem FPI = LogManager.GetStreamWriter(item.type);
                        FPI._StreamWriter.WriteLine(item.Content);
                        FPI._nCurFileLength += (long)Encoding.UTF8.GetByteCount(item.Content);
                        nOutputLogCount++;
                        if (nOutputLogCount > 500)
                        {
                            nOutputLogCount = 0;
                            Thread.Sleep(1);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                string exstr = ex.ToString();
            }
        }

        public static void WaitExitServer()
        {
            _NeedExitServer = true;
            MyConsole.WriteLineEx("等待LOG全部输出...." + _LogItems.Count.ToString(), ConsoleColor.Green);
            while (_LogItems.Count != 0)
            {
                Thread.Sleep(1);
            }
            MyConsole.WriteLineEx("LOG全部输出完毕！", ConsoleColor.Green);
        }

        private static void WriteLogEx(string strLogMsg)
        {
            if (!_NeedExitServer)
            {
                try
                {
                    if (EnableDbgView)
                    {
                        Debug.WriteLine(strLogMsg);
                    }
                    LogItem newLogItem = new LogItem()
                    {
                        type = LogTypes.Info,
                        strMsg = strLogMsg
                    };
                    _LogItems.Enqueue(newLogItem);
                }
                catch (Exception ex)
                {
                    string exstr = ex.ToString();
                }
            }
        }

        private static void _WriteException(string exceptionMsg)
        {
            WriteLog("##exception##\r\n" + exceptionMsg, null, true);
        }

        public static void DisableLogTypes(int[] logTypes)
        {
            LogManager.LogFlagsArray.SetAll(true);
            if (null != logTypes)
            {
                foreach (int logtype in logTypes)
                {
                    if (logtype < 1002 && logtype >= 0)
                    {
                        LogManager.LogFlagsArray.Set(logtype, false);
                    }
                }
            }
        }

        public static void WriteLog(string logMsg, Exception ex = null, bool bConsole = false)
        {
            WriteLogEx(logMsg);
            if (bConsole)
            {
                MyConsole.WriteLineEx(logMsg, ConsoleColor.Green);
            }
            if (ex != null)
            {
                WriteException(ex.ToString());
            }
        }

        public static void WriteException(string exceptionMsg)
        {
            LogManager._WriteException(exceptionMsg);
        }

        public static void WriteStackTrace()
        {
            string strStackInfo = "";
            StackTrace st = new StackTrace(true);
            StackFrame[] sf = st.GetFrames();
            for (int i = 0; i < sf.Length; i++)
            {
                string strLineInfo = string.Format("{0}({1}) {2}.{3}\r\n", new object[]
                {
                    sf[i].GetFileName(),
                    sf[i].GetFileLineNumber(),
                    sf[i].GetMethod().DeclaringType.FullName,
                    sf[i].GetMethod().Name
                });
                strStackInfo += strLineInfo;
            }
            LogManager._WriteException(strStackInfo);
        }

        private static string FindTheLastFile(LogTypes logType)
        {
            string strRes = "";
            string strTypeName = logType.ToString();
            try
            {
                string[] files = Directory.GetFiles(LogManager.LogPath);
                long nMaxTime = 0L;
                foreach (string it in files)
                {
                    if (it.Contains(strTypeName))
                    {
                        string strFileName = it;
                        int nIndex = strFileName.LastIndexOf('.');
                        string strExFileName = strFileName.Substring(nIndex + 1);
                        if (!(strExFileName != "log"))
                        {
                            strFileName = strFileName.Substring(0, nIndex);
                            nIndex = strFileName.LastIndexOf('_');
                            if (nIndex >= 0)
                            {
                                string strHMS = strFileName.Substring(nIndex + 1);
                                strFileName = strFileName.Substring(0, nIndex);
                                nIndex = strFileName.LastIndexOf('_');
                                if (nIndex >= 0)
                                {
                                    string strYMD = strFileName.Substring(nIndex + 1);
                                    string strNowYMD = DateTime.Now.ToString("yyyyMMdd");
                                    if (!(strYMD != strNowYMD))
                                    {
                                        strFileName = strFileName.Substring(0, nIndex);
                                        nIndex = strFileName.LastIndexOf('/');
                                        if (nIndex >= 0)
                                        {
                                            string strType = strFileName.Substring(nIndex + 1);
                                            if (!(strType != strTypeName))
                                            {
                                                string strTime = strYMD + strHMS;
                                                if (long.TryParse(strTime, out long nTime))
                                                {
                                                    if (nTime > nMaxTime)
                                                    {
                                                        nMaxTime = nTime;
                                                        strRes = it;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            finally
            {
            }
            return strRes;
        }

        private static LogFilePoolItem GetStreamWriter(LogTypes logType)
        {
            DateTime now = DateTime.Now;
            if (!LogManager.LogType2FileDict.TryGetValue(logType, out LogFilePoolItem item))
            {
                item = new LogFilePoolItem();
                string strLastFile = LogManager.FindTheLastFile(logType);
                if (strLastFile != "" && File.Exists(strLastFile))
                {
                    item._StreamWriter = File.AppendText(strLastFile);
                    item.OpenTimeOnHours = (long)now.Hour;
                    item.OpenTimeOnDayOfYear = (long)now.DayOfYear;
                    item._StreamWriter.AutoFlush = true;
                    item._strCurFileName = strLastFile;
                    FileInfo fileInfo = new FileInfo(strLastFile);
                    item._nCurFileLength = fileInfo.Length;
                }
                LogManager.LogType2FileDict.Add(logType, item);
            }
            if ((long)now.Hour != item.OpenTimeOnHours)
            {
                if (null != item._StreamWriter)
                {
                    item._StreamWriter.Flush();
                    item._StreamWriter.Close();
                    item._StreamWriter = null;
                    if (item._strCurFileName != "" && File.Exists(item._strCurFileName))
                    {
                        item._StreamWriter = File.AppendText(item._strCurFileName);
                        item._StreamWriter.AutoFlush = true;
                        item.OpenTimeOnHours = (long)now.Hour;
                    }
                }
            }
            if ((long)now.DayOfYear != item.OpenTimeOnDayOfYear || item._StreamWriter == null || (LogManager.LogFileMaxSize > 0L && item._nCurFileLength >= LogManager.LogFileMaxSize))
            {
                if (null != item._StreamWriter)
                {
                    item._StreamWriter.Flush();
                    item._StreamWriter.Close();
                    item._StreamWriter = null;
                }
                string file;
                if (LogManager.LogFileMaxSize > 0L)
                {
                    file = string.Concat(new string[]
                    {
                        LogManager.LogPath,
                        logType.ToString(),
                        "_",
                        now.ToString("yyyyMMdd"),
                        "_",
                        now.ToString("HHmmss"),
                        ".log"
                    });
                }
                else
                {
                    file = string.Concat(new string[]
                    {
                        LogManager.LogPath,
                        logType.ToString(),
                        "_",
                        now.ToString("yyyyMMdd"),
                        ".log"
                    });
                }
                item._StreamWriter = File.AppendText(file);
                item.OpenTimeOnHours = (long)now.Hour;
                item.OpenTimeOnDayOfYear = (long)now.DayOfYear;
                item._StreamWriter.AutoFlush = true;
                item._strCurFileName = file;
                try
                {
                    if (File.Exists(file))
                    {
                        FileInfo fileInfo = new FileInfo(file);
                        item._nCurFileLength = fileInfo.Length;
                    }
                    else
                    {
                        item._nCurFileLength = 0L;
                    }
                }
                catch (Exception)
                {
                }
            }
            return item;
        }

        public static void WriteExceptionUseCache(string exStr)
        {
            try
            {
                DateTime now = DateTime.Now;
                lock (LogManager.ExceptionCacheDict)
                {
                    if (now.Hour != LogManager.LastClearCacheTime.Hour || LogManager.ExceptionCacheDict.Count > 10000)
                    {
                        LogManager.ExceptionCacheDict.Clear();
                        LogManager.LastClearCacheTime = now;
                    }
                    if (!LogManager.ExceptionCacheDict.TryGetValue(exStr, out int count))
                    {
                        StackTrace stackTrace = new StackTrace(2, true);
                        LogManager.WriteException(exStr + stackTrace.ToString());
                    }
                    count = (LogManager.ExceptionCacheDict[exStr] = count + 1);
                }
            }
            catch (Exception ex)
            {
                LogManager.WriteException(ex.ToString());
            }
        }

        private static long _logFileMaxSize = 0L;

        private static Dictionary<string, int> ExceptionCacheDict = new Dictionary<string, int>();

        private static DateTime LastClearCacheTime;

        private static Dictionary<LogTypes, LogFilePoolItem> LogType2FileDict = new Dictionary<LogTypes, LogFilePoolItem>();

        private static ConcurrentQueue<LogItem> _LogItems = new ConcurrentQueue<LogItem>();

        private static bool EnableDbgView = false;

        public static int LogZoneID = 0;

        private static string _LogPath = string.Empty;

        private static string _ExceptionPath = string.Empty;

        private static DateTime _tLastWriteCPU = DateTime.Now;

        private static int nOutputLogCount = 0;

        private static bool _NeedExitServer = false;

        private static BitArray LogFlagsArray = new BitArray(1002, true);
    }
}
